#!/usr/bin/env bash

# 构建随机的C/S关系还是所有的C/S关系
RANDOM_CS=y

#
# 客户端-服务端的关系根据以下规则构建：
# 1、每个物理节点的每个VM的每个子网的IP都会作为客户端IP
# 2、每个客户端IP对应的服务端IP包括：
# 构建随机的C/S关系时：
# 2-1、分别从每个物理节点随机选取两个服务端VM，从其中一个VM选取相同子网的IP，从另一个VM随机选取一个不同子网的IP
# 2-2、在客户端VM的同一个物理节点随机选取服务端VM时，选取与客户端VM不同的VM，除非该物理节点只有一个VM
# 构建所有的C/S关系时：
# 2-1、所有VM的所有子网的IP，但如果客户端VM所在物理节点不止一个VM时，则服务端VM不包括与客户端VM相同的VM
#

function usage()
{
	echo -e "usage: $0 [file list]"
	echo -e "\tThe file format is 2D table, e.g.:"
	echo -e "\tvm1-net1-ip vm1-net2-ip ..."
	echo -e "\tvm2-net1-ip vm2-net2-ip ..."
	echo -e "\t..."
}

(($# < 1)) && usage && exit

#
# 分别读取各个物理节点所有VM的IP到对应的数组中
#
# 文件内容格式：
# 1、每个物理节点单独一个文件，包含该物理节点所有VM的IP，每个VM单独一行。
# 2、文件每行包含同一个VM所有子网的IP，每个VM每个子网包含一个IP，
#    各个子网的IP用空格分隔成多列，每个子网对应一列。
# 3、所有VM相同子网的IP在所有文件中都是在同一列。
# 例如：node-x包含2个VM（vm1和vm2），每个VM在两个子网（net1和net2）各有一个IP
# 则node-x文件内容如下：
# vm1-net1-ip vm1-net2-ip
# vm2-net1-ip vm2-net2-ip
#
# 各个文件依次读取到node0、node1、...数组中
NODE_PREFIX="node"
NODE_COUNT=0
for filename in "$@"; do
	mapfile -t ${NODE_PREFIX}${NODE_COUNT} < <(grep -v '^[[:space:]]*$' ${filename})
	((NODE_COUNT++))
done

#
# 随机输出指定数组中的一个值，但指定下标对应的值除外。
# 如果原数组为空，则输出空；
# 如果排除指定下标的值后数组为空，则输出原数组第一个值；
# 参数：<数组名称> [要排除的下标]
#
function random_next()
{
	local array_name="$1"
	local exclude_index=$2
	# 通过名称引用读取原数组, 原数组为空时输出空值
	declare -n origin_array="${array_name}"
	((${#origin_array[@]} == 0)) && echo "" && return
	# 不指定排除的下标时随机输出原数组的任一个值
	[[ -z "${exclude_index}" ]] && echo "${origin_array[$((${RANDOM} % ${#origin_array[@]}))]}" && return
	# 使用临时数组执行删除操作，删除后数组为空时输出原数组第一个值
	local tmp_array=("${origin_array[@]}")
	unset tmp_array[${exclude_index}]
	((${#tmp_array[@]} == 0)) && echo "${origin_array[0]}" && return
	# 重新生成结果数组，随机输出该数组中的任一个值
	local result_array=("${tmp_array[@]}")
	echo "${result_array[$((${RANDOM} % ${#result_array[@]}))]}"
}

function make_net_random_cs()
{
	local current_net_idx=$1
	local client_ip=${current_vm[${current_net_idx}]}
	local exclude_vm_idx
	local server_node
	local server_vm
	local server_ip
	local node_idx
	for ((node_idx = 0; node_idx < ${NODE_COUNT}; node_idx++)); do
		server_node="${NODE_PREFIX}${node_idx}"
		((${node_idx} == ${current_node_idx})) && exclude_vm_idx=${current_vm_idx} || exclude_vm_idx=
		# 同子网
		server_vm=($(random_next "${server_node}" ${exclude_vm_idx}))
		if ((${#server_vm[@]} > 0)); then
			server_ip=${server_vm[$((${current_net_idx} % ${#server_vm[@]}))]}
			echo "${client_ip} ${server_ip}"
		fi
		# 不同子网
		server_vm=($(random_next "${server_node}" ${exclude_vm_idx}))
		# 存在多个子网时才建立不同子网的关系
		if ((${#server_vm[@]} > 1)); then
			server_ip=$(random_next "server_vm" ${current_net_idx})
			echo "${client_ip} ${server_ip}"
		fi
	done
}

function make_net_full_cs()
{
	local current_net_idx=$1
	local client_ip=${current_vm[${current_net_idx}]}
	local node_idx
	for ((node_idx = 0; node_idx < ${NODE_COUNT}; node_idx++)); do
		declare -n server_node="${NODE_PREFIX}${node_idx}"
		local vm_idx
		for ((vm_idx = 0; vm_idx < ${#server_node[@]}; vm_idx++)); do
			((${node_idx} == ${current_node_idx})) && \
				((${vm_idx} == ${current_vm_idx})) && \
				((${#server_node[@]} > 1)) && continue
			local server_vm=(${server_node[${vm_idx}]})
			local net_idx
			for ((net_idx = 0; net_idx < ${#server_vm[@]}; net_idx++)); do
				local server_ip=${server_vm[${net_idx}]}
				echo "${client_ip} ${server_ip}"
			done
		done
	done
}

function make_vm_cs()
{
	local current_vm_idx=$1
	local current_vm=(${current_node[${current_vm_idx}]})
	local net_idx
	for ((net_idx = 0; net_idx < ${#current_vm[@]}; net_idx++)); do
		[[ "${RANDOM_CS}" == "y" ]] && make_net_random_cs ${net_idx} || \
			make_net_full_cs ${net_idx}
	done
}

function make_node_cs()
{
	local current_node_idx=$1
	declare -n current_node="${NODE_PREFIX}${current_node_idx}"
	local vm_idx
	for ((vm_idx = 0; vm_idx < ${#current_node[@]}; vm_idx++)); do
		make_vm_cs ${vm_idx}
	done
}

for ((i = 0; i < ${NODE_COUNT}; i++)); do
	make_node_cs ${i}
done
